<?php

	class XmlExporter implements IArrayExporter
	{
		public function __construct()
		{			
		}
		
		/**
		 * Exports xml from array and returns xml's dom document.
		 * 
		 * @param array $xmlData
		 * @return DOMDocument
		 */
		public function arrayToXmlDocument(array $xmlData)
		{
			$doc = new DOMDocument('1.0', 'UTF-8');
            $doc->formatOutput = true;
            $this->arrayToXmlImpl($xmlData, $doc);
            
            return $doc;
		}
		
		/**
		 * Exports xml from array and returns xml string.
		 * 
		 * @param array $xmlData
		 * @return string
		 */
		public function export(array $xmlData)
		{
			return $this->arrayToXmlDocument($xmlData)->saveXML();
		}
		
		/**
		 * Implementation of exporting xml from array.
		 * 
		 * @param array $xmlData
		 * @param DOMDocument $doc
		 * @param DOMElement $curNode
		 * @param string $parentNodeName
		 */
		protected function arrayToXmlImpl(array $xmlData, DOMDocument $doc, DOMElement $curNode = null, $parentNodeName = null)
		{
			if ($curNode == null)
            {
                $curNode = $doc;
            }
            
			foreach ($xmlData as $nodeName => $content)
            {
            	if (is_null($content))
                {
                	continue;
                }
            	
				if (is_numeric($nodeName))
                {
                	if (!$this->contentIsNode($content))
                	{
                    	$nodeName = $parentNodeName;
                	}
                	else
                	{
                		$nodeName = $content['node_name'];
                		$content  = $content['node_content'];
                	}
                }
                else if ($nodeName == 'attributes')
                {
                	foreach ($content as $attrName => $attrValue)
                	{
                		$curNode->setAttribute($attrName, $this->prepareTextNodeContent($attrValue));
                	}
                	continue;
                }
                
                if (!(is_array($content) && !$this->isAssocArray($content) 
                	&& count($content) > 0 && !$this->contentIsNode($content[0])))
                {
	                $node = $doc->createElement($nodeName);
	                $curNode->appendChild($node);
                }
                else
                {
                	$node = $curNode;
                }
                                
                if (is_array($content))
                {
                    $this->arrayToXmlImpl($content, $doc, $node, $nodeName);
                }
                else
                {
                	$textNode = $doc->createTextNode($this->prepareTextNodeContent($content));
                    $node->appendChild($textNode);
                }
            }
		}
		
		/**
		 * Prepares content to inserting in text node.
		 * 
		 * @param mixed $content
		 * @return string
		 */
		protected function prepareTextNodeContent($content)
		{
			if ($content instanceof DateTime)
			{
				$content = $content->format('c');
			}
			else if (is_bool($content))
			{
				$content = ($content ? 'true' : 'false');
			}
			return $content . '';
		}
		
		/**
         * Checks is given array is assoc.
         *
         * @param array $array
         * @return bool
         */
        protected function isAssocArray($array)
        {
        	return (is_array($array) && 0 !== count(array_diff_key($array, array_keys(array_keys($array)))));
        }
        
        /**
         * Checkes is content is node in array(node_name, node_content) form.
         * 
         * @param mixed $content
         * @return bool
         */
        protected function contentIsNode($content)
        {
        	return is_array($content) && isset($content['node_name']) && isset($content['node_content']);
        }
	}

?>